/* 
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Project: MyMANET - A Platform to Build Customized MANET's
Developers: Ashwin Paranjpe, Santosh Vempala
Georgia Institute of Technology, Atlanta, USA
*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdint.h>	//for uint8_t and uint32_t
#include<sys/socket.h>
#include<features.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<net/if.h>
#include<time.h>

#define MANIFOLD_APPL_TYPE 0x3636
#define BUFFER_LEN 512
#define MAX_NUM_OF_HOSTS 20
#define MAX_NUM_OF_STAT_ENTRIES 20
#define DEBUG 0
#define NUM_OF_REAL_HOSTS 6
#define ACTIVE_TIMEOUT 20
#define MSG_SIZE 40

uint8_t g_broadcast_mac[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
static uint8_t g_mac[6];

struct connections{
	uint8_t originator_mac[6];
	char timestamp[10];
}conn_ts[10];
int count_connections=0;

void update_timestamp(uint8_t originator_mac[6], char * timestamp)
{
	int i=0, j=0, done=0;
	for(i = 0; i < count_connections; i++){
		if(memcmp(originator_mac, conn_ts[i].originator_mac, 6) == 0){
			strncpy(conn_ts[i].timestamp, timestamp, 10);
			done=1;
		}
	}
	/*Connection record does not exist, add it.*/
	if((done != 1) && ((i == count_connections) || count_connections == 0)){
		memcpy(conn_ts[count_connections].originator_mac, originator_mac, 6);
		strncpy(conn_ts[count_connections].timestamp, timestamp, 10);
		count_connections++;
	}
}
int msg_is_duplicate(char *timestamp, uint8_t originator_mac[6])
{
	int i=0, j=0;

	if(count_connections == 0)
		return 0;

	for(i = 0; i < count_connections; i++){
		if(memcmp(originator_mac, conn_ts[i].originator_mac, 6) == 0){
			if(strncmp(timestamp, conn_ts[i].timestamp, 10) <= 0)
				return 1; /*Received timestamp is less than or equal to stored timestamp. Discard the message.*/
		}
	}
	
	return 0;	/*Not a duplicate*/
}

int CreateRawSocket(int protocol_to_sniff)
{
	int rawsock;

	if((rawsock = socket(PF_PACKET, SOCK_RAW, htons(protocol_to_sniff)))== -1)
	{
		perror("Error creating raw socket: ");
		exit(-1);
	}

	return rawsock;
}

int BindRawSocketToInterface(char *device, int rawsock, int protocol)
{

	struct sockaddr_ll sll;
	struct ifreq ifr;

	bzero(&sll, sizeof(sll));
	bzero(&ifr, sizeof(ifr));

	/*Copy device name*/
	strncpy((char *)ifr.ifr_name, device, IFNAMSIZ);

	/*Get hardware address */
	if (ioctl(rawsock, SIOCGIFHWADDR, &ifr) < 0)
	{
		printf("ioctl Error: SIOCGIFHWADDR");
		exit(1);
	}

	memset(g_mac, 0, 6);
	memcpy(g_mac, ifr.ifr_hwaddr.sa_data, 6);
#if DEBUG
	printf("\nMy Mac is : %x:%x:%x:%x:%x:%x\n", g_mac[0], g_mac[1], g_mac[2], g_mac[3], g_mac[4], g_mac[5]);
#endif

	/*Get the Interface Index  */
	if((ioctl(rawsock, SIOCGIFINDEX, &ifr)) == -1)
	{
		printf("Error getting Interface index !\n");
		exit(-1);
	}

	/*Bind Raw socket to this interface */
	sll.sll_family = AF_PACKET;
	sll.sll_ifindex = ifr.ifr_ifindex;
	sll.sll_protocol = htons(protocol); 


	if((bind(rawsock, (struct sockaddr *)&sll, sizeof(sll)))== -1)
	{
		perror("Error binding raw socket to interface\n");
		exit(-1);
	}

	return 1;

}

void PrintPacketInHex(unsigned char *packet, int len)
{
	unsigned char *p = packet;

	printf("\n\n---------Packet---Starts----\n\n");

	while(len--)
	{
		printf("%.2x ", *p);
		p++;
	}

	printf("\n\n--------Packet---Ends-----\n\n");

}


PrintInHex(const char *mesg, unsigned char *p, int len)
{
	printf("%s", mesg);

	while(len--)
	{
		printf("%.2X ", *p);
		p++;
	}

}

ParseEthernetPacket(unsigned char *packet, int len)
{
	struct ethhdr *ethernet_header;
	uint32_t ip_long_nl=0;
	char node_name[10]="";
	char msg[MSG_SIZE]="";
	char timestamp[20]="";
	uint32_t pkts_network = 0;
	uint32_t session_network = 0;
	uint32_t tx_pkts_network = 0;
	uint32_t tx_session_network = 0;
	uint8_t originator_mac[6]={0};
	int retval=0;

	if( len < (sizeof(struct ethhdr) + (sizeof(uint8_t) * 6) + (sizeof(uint32_t) * 2)) )	{
		printf("Packet size too small !\n");
	}
	else {
		ethernet_header = (struct ethhdr *)packet;
#if DEBUG
		/* First set of 6 bytes are Destination MAC */
		PrintInHex("Destination MAC: ", ethernet_header->h_dest, 6);
		printf("\n");

		/* Second set of 6 bytes are Source MAC */
		PrintInHex("Source MAC: ", ethernet_header->h_source, 6);
		printf("\n");

		/* Last 2 bytes in the Ethernet header are the protocol it carries */
		PrintInHex("Protocol: ",(void *)&ethernet_header->h_proto, 2);
		printf("\n");
#endif

		if(ethernet_header->h_proto != MANIFOLD_APPL_TYPE){
#if DEBUG
			printf("\n\nNot our packet\n\n");
#endif
			//return;
		}

		if(memcmp(ethernet_header->h_dest, g_broadcast_mac, 6) != 0) {
#if DEBUG
			printf("\n\nNot our packet\n\n");
#endif
		//	return;
		}

		packet = packet + sizeof(struct ethhdr);
		len -= sizeof(struct ethhdr);

		memcpy(originator_mac, packet, 6);

		packet = packet + 6;
		len -= 6;

		memcpy(&ip_long_nl, packet, sizeof(uint32_t));

		packet = packet + sizeof(uint32_t);
		len -= sizeof(uint32_t);

		memcpy(node_name, packet, 10);

		packet = packet + 10;
		len -= 10;

		memcpy(msg, packet, MSG_SIZE);
		strncpy(timestamp, msg, 8);
		timestamp[8] = 0;
		
		packet = packet + MSG_SIZE;
		len -= MSG_SIZE;

		if(!msg_is_duplicate(timestamp, originator_mac)){
	
			printf("\n%s : %s\n", node_name, msg);
		}
		else {
			printf("\n*\n");
		}

		update_timestamp(originator_mac, timestamp);

	}
}

/*argv[1] is the interface*/
/*argv[2] is the TOLERANCE parameter for kernel-space distance table updates.*/

main(int argc, char **argv)
{
	int raw;
	unsigned char packet_buffer[BUFFER_LEN]; 
	int len;
	struct sockaddr_ll packet_info;
	int packet_info_size = sizeof(packet_info);
	struct timeval timeout;
	int retval=0;
	

	if(argc != 2){
		printf("\nusage: sudo ./recv [INTERFACE]\n\n");
		exit(0);
	}

	/*Create Raw socket */
	raw = CreateRawSocket(MANIFOLD_APPL_TYPE);

	/*Bind socket to interface */
	BindRawSocketToInterface(argv[1], raw, MANIFOLD_APPL_TYPE);

	/*Start Sniffing*/
	while(1)
	{
		len = 0;
		retval=0;
		memset(packet_buffer, 0, BUFFER_LEN);
		timeout.tv_sec = 0;
		timeout.tv_usec = 0;
		fd_set fds;
		FD_ZERO(&fds);
		FD_SET(raw, &fds);

		retval = select((raw+1), &fds, 0, 0, &timeout);

		if(retval){
			len = recvfrom(raw, packet_buffer, BUFFER_LEN, 0, (struct sockaddr*)&packet_info, &packet_info_size);
		}
		else if(retval == -1){
			perror("select():");
		}
		else{
			sleep(1);
		}
		
		if(len == -1)
		{
			perror("Recv from returned -1: ");
			exit(-1);
		}
		else if(len > 0)
		{
#if DEBUG	
			/*A Packet has been received*/
			PrintPacketInHex(packet_buffer, len);
#endif

			/*Parse Ethernet Packet*/
			ParseEthernetPacket(packet_buffer, len);

		}
	}


	return 0;
}


